﻿using Ewk.Math.Numerics;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Ewk.Math.ComputationTypes.UnitTests
{
    [TestClass]
    public class FunctionUnitTests
    {
        [TestMethod]
        public void Function_addition_results_in_adding_the_results_of_the_functions()
        {
            var a = new Function<double, double>(input => input * 3);
            var b = new Function<double, double>(input => input * 5);

            var result = a + b;

            // a(x) + b(x) = x * 3 + x * 5 = x * (3 + 5) = x * 8
            const int x = 7;
            Assert.AreEqual(x * 8, result.Func(x).Value);
        }

        [TestMethod]
        public void Function_subtraction_results_in_subtracting_the_results_of_the_functions()
        {
            var a = new Function<double, double>(input => input * 3);
            var b = new Function<double, double>(input => input * 5);

            var result = a - b;

            // a(x) - b(x) = x * 3 - x * 5 = x * (3 - 5) = x * -2
            const int x = 7;
            Assert.AreEqual(x * -2, result.Func(x).Value);
        }

        [TestMethod]
        public void Function_multiplication_results_in_multiplying_the_results_of_the_functions()
        {
            var a = new Function<double, double>(input => input + 3);
            var b = new Function<double, double>(input => input + 5);

            var result = a * b;

            // a(x) * b(x) = (x + 3) * (x + 5) = x^2 + 3x + 5x + 3*5 = x^2 + 8x + 15
            const int x = 7;
            Assert.AreEqual(x*x + 8*x + 15, result.Func(x).Value);
        }

        [TestMethod]
        public void Function_divide_results_in_dividing_the_results_of_the_functions()
        {
            var a = new Function<double, double>(input => Computable<double>.Pow(input, 5));
            var b = new Function<double, double>(input => Computable<double>.Pow(input, 3));

            var result = a / b;

            // a(x) / b(x) = x^5 / x^3 = x^2
            const int x = 7;
            Assert.AreEqual(x * x, result.Func(x).Value);
        }

        [TestMethod]
        public void Function_chaining_results_in_applying_the_results_of_the_innerFunction_to_the_input_of_the_outFunction()
        {
            var a = new Function<double, double>(input => input * 3);
            var b = new Function<double, double>(input => input + 5);

            var result1 = Function<double, double>.Chain(a, b);
            var result2 = Function<double, double>.Chain(b, a);

            // chain a(b(x)) = a(x+5) = (x+5) * 3 = 3x + 15
            // chain b(a(x)) = b(3x) = 3x + 5
            const int x = 7;
            Assert.AreEqual(3 * x + 15, result1.Func(x).Value);
            Assert.AreEqual(3 * x + 5, result2.Func(x).Value);
        }

        [TestMethod]
        public void Function_can_transform_the_input_to_another_space()
        {
            var a = new Function<double, Complex>(input => new Complex(input.Value * 2, input.Value/ 2));

            const int x = 7;
            Assert.AreEqual(14, a.Func(x).Value.Real);
            Assert.AreEqual(3.5, a.Func(x).Value.Imaginary);
        }
    }
}
